﻿/*
COPYRIGHT (C) 2010-2011 Raynald Messié

THIS FILE IS PART OF SCRUMPILOT.

SCRUMPILOT IS FREE SOFTWARE: YOU CAN REDISTRIBUTE IT AND/OR MODIFY IT UNDER THE TERMS OF 
THE GNU LESSER GENERAL PUBLIC LICENSE VERSION v2.1 AS PUBLISHED BY THE FREE SOFTWARE FOUNDATION.

SCRUMPILOT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY; WITHOUT
EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU LESSER
GENERAL PUBLIC LICENSE FOR MORE DETAILS.

YOU SHOULD HAVE RECEIVED A COPY OF THE GNU LESSER GENERAL PUBLIC LICENSE ALONG WITH SCRUMPILOT. 
IF NOT, SEE <HTTP://WWW.GNU.ORG/LICENSES/>.
*/

using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.VersionControl.Common;
using ScrumPilot.Helpers;

namespace ScrumPilot.TFSHelpers
{
    public partial class TFSServices
    {
        #region Events

        /// <summary>
        /// Subscribe to an event on TfsServer instance
        /// </summary>
        /// <returns>Subscrition ID (use for unsubscribe)</returns>
        public int SubscribeEvent(EventTypes type, String webServiceUrl, String token)
        {
            int iRet = -1;
            if (Authenticate())
            {
                String sType = String.Empty;
                switch (type)
                {
                    case EventTypes.Unknown:
                        break;
                    case EventTypes.AclChangedEvent:
                        break;
                    case EventTypes.Branchmovedevent:
                        break;
                    case EventTypes.BuildCompletionEvent:
                        break;
                    case EventTypes.BuildStatusChangeEvent:
                        break;
                    case EventTypes.CommonStructureChangedEvent:
                        break;
                    case EventTypes.DataChangedEvent:
                        break;
                    case EventTypes.IdentityChangedEvent:
                        break;
                    case EventTypes.IdentityCreatedEvent:
                        break;
                    case EventTypes.IdentityDeletedEvent:
                        break;
                    case EventTypes.MembershipChangedEvent:
                        break;
                    case EventTypes.WorkItemChangedEvent:
                        sType = "WorkItemChangedEvent";
                        break;
                    case EventTypes.CheckinEvent:
                        sType = "CheckinEvent";
                        break;
                }
                if (!String.IsNullOrEmpty(sType))
                {
                    iRet = TfsEventService.SubscribeEvent(_tfs.AuthorizedIdentity.Descriptor.Identifier,
                                                               sType, "",
                                                               new DeliveryPreference
                                                                   {
                                                                       Type = DeliveryType.Soap,
                                                                       Address = webServiceUrl,
                                                                       Schedule = DeliverySchedule.Immediate
                                                                   });
                }
            }
            return iRet;
        }

        /// <summary>
        /// Unsubscribe to an event
        /// </summary>
        /// <param name="subscribeID">Subscription ID</param>
        public void UnSubscribeEvent(int subscribeID)
        {
            TfsEventService.UnsubscribeEvent(subscribeID);
        }

        #endregion

        /// <summary>
        /// Show config on TFS 2010
        /// </summary>
        public void ShowConfig()
        {
            TfsConfigurationServer configurationServer = TfsServer.ConfigurationServer;
            if (configurationServer != null) // In case of TFS 2008 or less
            {
                // Get the catalog of team project collections
                ReadOnlyCollection<CatalogNode> collectionNodes = configurationServer.CatalogNode.QueryChildren(
                    new[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None);

                // List the team project collections
                foreach (CatalogNode collectionNode in collectionNodes)
                {
                    // Use the InstanceId property to get the team project collection
                    var collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]);
                    TfsTeamProjectCollection teamProjectCollection =
                        configurationServer.GetTeamProjectCollection(collectionId);

                    // Print the name of the team project collection
                    Console.WriteLine("Collection: " + teamProjectCollection.Name);

                    // Get a catalog of team projects for the collection
                    ReadOnlyCollection<CatalogNode> projectNodes = collectionNode.QueryChildren(
                        new[] { CatalogResourceTypes.TeamProject },
                        false, CatalogQueryOptions.None);

                    // List the team projects in the collection
                    foreach (CatalogNode projectNode in projectNodes)
                    {
                        Console.WriteLine(" Team Project: " + projectNode.Resource.DisplayName);
                    }
                }
            }
        }

        public void ChangeTFSConfiguration(String key, String value)
        {
            TfsConfigurationServer configurationServer = TfsServer.ConfigurationServer;
            if (configurationServer != null) // In case of TFS 2008 or less
            {
                var rw = configurationServer.GetService<ITeamFoundationRegistry>();
                rw.SetValue(key, value);
            }
        }

        /// <summary>
        /// Change the events notification log level in main config
        /// http://blogs.msdn.com/b/granth/archive/2009/10/28/tfs2010-diagnosing-email-and-soap-subscription-failures.aspx
        /// </summary>
        /// <param name="level">Level : 0 min, 2 detail</param>
        public void ChangeLevelLog(int level)
        {
            ChangeTFSConfiguration("/Service/Integration/Settings/NotificationJobLogLevel", level.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// Change the events notification delay in main config
        /// http://blogs.msdn.com/b/chrisid/archive/2010/03/05/faster-delivery-of-notifications.aspx
        /// </summary>
        /// <param name="delay">Delay in second</param>
        public void ChangeDelaNotification(int delay)
        {
            ChangeTFSConfiguration("/Service/Integration/Settings/NotificationJobDelay", delay.ToString(CultureInfo.InvariantCulture));
        }

        #region Event Objects

        public TFSWallContentObject GetWallContent(String xml, int iType, Int64 id, String tfsRoorUrl)
        {
            TFSWallContentObject tfsR = null;
            switch (iType)
            {
                case 1: // Checkin
                    var chk = DeserializeChangedEvent<CheckinEvent>(xml);
                    if (chk != null)
                    {
                        tfsR = new TFSWallContentObject
                                   {
                                       Name = String.IsNullOrWhiteSpace(chk.Comment) ? chk.ContentTitle : chk.Comment,
                                       More = String.Empty,
                                       Url = tfsRoorUrl + "_versionControl/changeset?id=" + chk.Number,
                                       ProjectName = chk.TeamProject
                                   };
                        String artifacts = (from ClientArtifact t in chk.Artifacts where !String.IsNullOrEmpty(t.Item) select t).Aggregate("", (current, t) => current + (t.Item + ", "));

                        if (artifacts.Length > 0)
                        {
                            tfsR.More += artifacts.Substring(0, artifacts.Length - 2);
                        }
                    }
                    break;
                case 2: //WorkItem Changed
                    var wik = DeserializeChangedEvent<WorkItemChangedEvent>(xml);
                    if (wik != null)
                    {
                        int witId = wik.CoreFields.IntegerFields.First(p => p.Name == "ID").NewValue;
                        var workItem = this.GetWorkItem(witId, false);
                        tfsR = new TFSWallContentObject
                                   {
                                       ID = witId,
                                       Name = wik.WorkItemTitle,
                                       Url = tfsRoorUrl + wik.PortfolioProject + "/_workItems#_a=edit&id=" + wik.CoreFields.IntegerFields.First(p => p.Name == "ID").NewValue,
                                       Descriptions = String.Empty,
                                       ProjectName = wik.PortfolioProject,
                                       Iteration = workItem.IterationPath,
                                       WorkItemType = workItem.Type.Name
                                   };

                        StringField state =
                            wik.CoreFields.StringFields.FirstOrDefault(p => p.ReferenceName == "System.State" &&
                                                                            p.NewValue != p.OldValue);
                        if (state != null) // Check if state changed
                        {
                            tfsR.ChangeState = true;
                            tfsR.Descriptions = String.Format("  State have been changed {0} => {1}", state.OldValue,
                                                       state.NewValue);
                            StringField reason = wik.CoreFields.StringFields.FirstOrDefault(p => p.ReferenceName == "System.Reason");
                            if (reason != null) tfsR.Descriptions += String.Format(" (Reason : {0})", reason.NewValue);

                            if ((workItem.GetWorkItemType() == WorkItemType.Task ||
                                workItem.GetWorkItemType() == WorkItemType.UserStory)
                                && workItem.GetCompletedWork() > 0)
                            {
                                if (workItem.GetOriginalEstimate() > 0)
                                {
                                    tfsR.HourStatus = String.Format("[{2}%](Est:{0}/Com:{1})",
                                        workItem.GetOriginalEstimate(),
                                        workItem.GetCompletedWork(),
                                        Math.Round((workItem.GetCompletedWork() / workItem.GetOriginalEstimate() * 100)));
                                }
                            }
                        }

                        StringField assigned =
                            wik.CoreFields.StringFields.FirstOrDefault(p => p.ReferenceName == "System.AssignedTo" &&
                                                                            (p.NewValue ?? "") != (p.OldValue ?? ""));
                        if (assigned != null) // check if assigned to changed
                            tfsR.Descriptions += !String.IsNullOrEmpty(assigned.NewValue)
                                             ? String.Format("  Assigned to {0}", assigned.NewValue)
                                             : "Unassigned";

                        /*if (state == null && assigned == null)
                            tfsR.Descriptions = wik.WorkItemTitle;*/
                    }
                    break;
            }
            return tfsR;
        }

        public void CheckUserStoryState(int id)
        {
            var wi = this.TfsWorkItemStore.GetWorkItem(id);
            if (wi.GetWorkItemType() == WorkItemType.Task)
            {
                var wiparent = wi.GetParentWorkItem(this);
                if (wiparent != null && wiparent.GetWorkItemType() == WorkItemType.UserStory)
                {
                    var lst = wiparent.GetChildWorkItem().Where(p => p.GetWorkItemType() == WorkItemType.Task).ToList();
                    var count = lst.Count;
                    if (lst.Count > 0)
                    {
                        var newState = WorkItemState.Todo;
                        var inrun = lst.Count(p => p.GetState() == WorkItemState.InProgress);
                        var done = lst.Count(p => p.GetState() == WorkItemState.Done);
                        if (done == count)// all sub-task done
                        {
                            newState = WorkItemState.Done;
                        }
                        else if (inrun > 0 || done > 0)
                        {
                            newState = WorkItemState.InProgress;
                        }
                        if (wiparent.GetState() != newState)
                        {
                            var wiToUpdate = this.GetWorkItem(wiparent.Id, true);
                            switch (newState)
                            {
                                case WorkItemState.InProgress:
                                    wiToUpdate.State = "Active";
                                    wiToUpdate.SetAssignedTo(wi.GetAssignedTo());
                                    break;
                                case WorkItemState.Done:
                                    wiToUpdate.State = "Resolved";
                                    break;
                            }
                            wiToUpdate.Save();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Deserialise an XML WorkItemChanged Event or XML Checkin Event
        /// </summary>
        /// <param name="pXmlizedString">XML String</param>
        /// <returns>WorkItemChangedEvent object or CheckinEvent object (null if it's not work)</returns>
        public T DeserializeChangedEvent<T>(String pXmlizedString)
        {
            return (T)DeserializeObject(pXmlizedString, new XmlSerializer(typeof(T)));
        }

        /// <summary>
        /// Deserialise an XML Event
        /// </summary>
        /// <param name="pXmlizedString">XML String</param>
        /// <param name="serializer"></param>
        /// <returns>an Object (null if it's not work)</returns>
        public static Object DeserializeObject(String pXmlizedString, XmlSerializer serializer)
        {
            XmlTextReader reader = null;
            StringReader sr = null;
            try
            {
                sr = new StringReader(pXmlizedString);
                reader = new XmlTextReader(sr);
                return serializer.Deserialize(reader);
            }
            catch
            {
                return null;
            }
            finally
            {
                if (sr != null) sr.Close();
                if (reader != null) reader.Close();
            }
        }

        #endregion
    }
}